{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "Ic4_occAAiAT"
      },
      "source": [
        "##### Copyright 2019 The TensorFlow Authors."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "cellView": "form",
        "colab": {},
        "colab_type": "code",
        "id": "ioaprt5q5US7"
      },
      "outputs": [],
      "source": [
        "#@title Licensed under the Apache License, Version 2.0 (the \"License\");\n",
        "# you may not use this file except in compliance with the License.\n",
        "# You may obtain a copy of the License at\n",
        "#\n",
        "# https://www.apache.org/licenses/LICENSE-2.0\n",
        "#\n",
        "# Unless required by applicable law or agreed to in writing, software\n",
        "# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
        "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
        "# See the License for the specific language governing permissions and\n",
        "# limitations under the License."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "cellView": "form",
        "colab": {},
        "colab_type": "code",
        "id": "yCl0eTNH5RS3"
      },
      "outputs": [],
      "source": [
        "#@title MIT License\n",
        "#\n",
        "# Copyright (c) 2017 François Chollet\n",
        "#\n",
        "# Permission is hereby granted, free of charge, to any person obtaining a\n",
        "# copy of this software and associated documentation files (the \"Software\"),\n",
        "# to deal in the Software without restriction, including without limitation\n",
        "# the rights to use, copy, modify, merge, publish, distribute, sublicense,\n",
        "# and/or sell copies of the Software, and to permit persons to whom the\n",
        "# Software is furnished to do so, subject to the following conditions:\n",
        "#\n",
        "# The above copyright notice and this permission notice shall be included in\n",
        "# all copies or substantial portions of the Software.\n",
        "#\n",
        "# THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n",
        "# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n",
        "# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n",
        "# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n",
        "# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n",
        "# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n",
        "# DEALINGS IN THE SOFTWARE."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "ItXfxkxvosLH"
      },
      "source": [
        "# 케라스와 텐서플로 허브를 사용한 영화 리뷰 텍스트 분류하기"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "yX7gdv5JgUWp"
      },
      "source": [
        "\u003ctable class=\"tfo-notebook-buttons\" align=\"left\"\u003e\n",
        "  \u003ctd\u003e\n",
        "    \u003ca target=\"_blank\" href=\"https://www.tensorflow.org/tutorials/keras/text_classification_with_hub\"\u003e\u003cimg src=\"https://www.tensorflow.org/images/tf_logo_32px.png\" /\u003eTensorFlow.org에서 보기\u003c/a\u003e\n",
        "  \u003c/td\u003e\n",
        "  \u003ctd\u003e\n",
        "    \u003ca target=\"_blank\" href=\"https://colab.research.google.com/github/tensorflow/docs/blob/master/site/ko/tutorials/keras/text_classification_with_hub.ipynb\"\u003e\u003cimg src=\"https://www.tensorflow.org/images/colab_logo_32px.png\" /\u003e구글 코랩(Colab)에서 실행하기\u003c/a\u003e\n",
        "  \u003c/td\u003e\n",
        "  \u003ctd\u003e\n",
        "    \u003ca target=\"_blank\" href=\"https://github.com/tensorflow/docs/blob/master/site/ko/tutorials/keras/text_classification_with_hub.ipynb\"\u003e\u003cimg src=\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\" /\u003e깃허브(GitHub) 소스 보기\u003c/a\u003e\n",
        "  \u003c/td\u003e\n",
        "\u003c/table\u003e"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "YJ49GhXrgUWq"
      },
      "source": [
        "Note: 이 문서는 텐서플로 커뮤니티에서 번역했습니다. 커뮤니티 번역 활동의 특성상 정확한 번역과 최신 내용을 반영하기 위해 노력함에도\n",
        "불구하고 [공식 영문 문서](https://www.tensorflow.org/?hl=en)의 내용과 일치하지 않을 수 있습니다.\n",
        "이 번역에 개선할 부분이 있다면\n",
        "[tensorflow/docs](https://github.com/tensorflow/docs) 깃헙 저장소로 풀 리퀘스트를 보내주시기 바랍니다.\n",
        "문서 번역이나 리뷰에 참여하려면\n",
        "[docs-ko@tensorflow.org](https://groups.google.com/a/tensorflow.org/forum/#!forum/docs-ko)로\n",
        "메일을 보내주시기 바랍니다."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "txOb1UvRgUWr"
      },
      "source": [
        "이 노트북은 영화 리뷰(review) 텍스트를 *긍정*(positive) 또는 *부정*(negative)으로 분류합니다. 이 예제는 *이진*(binary)-또는 클래스(class)가 두 개인- 분류 문제입니다. 이진 분류는 머신러닝에서 중요하고 널리 사용됩니다.\n",
        "\n",
        "이 튜토리얼에서는 텐서플로 허브(TensorFlow Hub)와 케라스(Keras)를 사용한 기초적인 전이 학습(transfer learning) 애플리케이션을 보여줍니다.\n",
        "\n",
        "여기에서는 [인터넷 영화 데이터베이스](https://www.imdb.com/)(Internet Movie Database)에서 수집한 50,000개의 영화 리뷰 텍스트를 담은 [IMDB 데이터셋](https://www.tensorflow.org/api_docs/python/tf/keras/datasets/imdb)을 사용하겠습니다. 25,000개 리뷰는 훈련용으로, 25,000개는 테스트용으로 나뉘어져 있습니다. 훈련 세트와 테스트 세트의 클래스는 *균형*이 잡혀 있습니다. 즉 긍정적인 리뷰와 부정적인 리뷰의 개수가 동일합니다.\n",
        "\n",
        "이 노트북은 텐서플로에서 모델을 만들고 훈련하기 위한 고수준 파이썬 API인 [tf.keras](https://www.tensorflow.org/guide/keras)와 전이 학습 라이브러리이자 플랫폼인 [텐서플로 허브](https://www.tensorflow.org/hub)를 사용합니다. `tf.keras`를 사용한 고급 텍스트 분류 튜토리얼은 [MLCC 텍스트 분류 가이드](https://developers.google.com/machine-learning/guides/text-classification/)를 참고하세요."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "colab": {},
        "colab_type": "code",
        "id": "2ew7HTbPpCJH"
      },
      "outputs": [],
      "source": [
        "from __future__ import absolute_import, division, print_function, unicode_literals\n",
        "\n",
        "import numpy as np\n",
        "\n",
        "!pip install tf-nightly-2.0-preview\n",
        "import tensorflow as tf\n",
        "\n",
        "import tensorflow_hub as hub\n",
        "import tensorflow_datasets as tfds\n",
        "\n",
        "print(\"버전: \", tf.__version__)\n",
        "print(\"즉시 실행 모드: \", tf.executing_eagerly())\n",
        "print(\"허브 버전: \", hub.__version__)\n",
        "print(\"GPU \", \"사용 가능\" if tf.config.experimental.list_physical_devices(\"GPU\") else \"사용 불가능\")"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "iAsKG535pHep"
      },
      "source": [
        "## IMDB 데이터셋 다운로드\n",
        "\n",
        "IMDB 데이터셋은 [텐서플로 데이터셋](https://github.com/tensorflow/datasets)(TensorFlow datasets)에 포함되어 있습니다. 다음 코드는 IMDB 데이터셋을 컴퓨터(또는 코랩 런타임)에 다운로드합니다:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "colab": {},
        "colab_type": "code",
        "id": "zXXx5Oc3pOmN"
      },
      "outputs": [],
      "source": [
        "# 훈련 세트를 6대 4로 나눕니다.\n",
        "# 결국 훈련에 15,000개 샘플, 검증에 10,000개 샘플, 테스트에 25,000개 샘플을 사용하게 됩니다.\n",
        "train_validation_split = tfds.Split.TRAIN.subsplit([6, 4])\n",
        "\n",
        "(train_data, validation_data), test_data = tfds.load(\n",
        "    name=\"imdb_reviews\", \n",
        "    split=(train_validation_split, tfds.Split.TEST),\n",
        "    as_supervised=True)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "l50X3GfjpU4r"
      },
      "source": [
        "## 데이터 탐색\n",
        "\n",
        "잠시 데이터 형태를 알아 보죠. 이 데이터셋의 샘플은 전처리된 정수 배열입니다. 이 정수는 영화 리뷰에 나오는 단어를 나타냅니다. 레이블(label)은 정수 0 또는 1입니다. 0은 부정적인 리뷰이고 1은 긍정적인 리뷰입니다.\n",
        "\n",
        "처음 10개의 샘플을 출력해 보겠습니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "colab": {},
        "colab_type": "code",
        "id": "QtTS4kpEpjbi"
      },
      "outputs": [],
      "source": [
        "train_examples_batch, train_labels_batch = next(iter(train_data.batch(10)))\n",
        "train_examples_batch"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "IFtaCHTdc-GY"
      },
      "source": [
        "처음 10개의 레이블도 출력해 보겠습니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "colab": {},
        "colab_type": "code",
        "id": "tvAjVXOWc6Mj"
      },
      "outputs": [],
      "source": [
        "train_labels_batch"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "LLC02j2g-llC"
      },
      "source": [
        "## 모델 구성\n",
        "\n",
        "신경망은 층을 쌓아서 만듭니다. 여기에는 세 개의 중요한 구조적 결정이 필요합니다:\n",
        "\n",
        "* 어떻게 텍스트를 표현할 것인가?\n",
        "* 모델에서 얼마나 많은 층을 사용할 것인가?\n",
        "* 각 층에서 얼마나 많은 *은닉 유닛*(hidden unit)을 사용할 것인가?\n",
        "\n",
        "이 예제의 입력 데이터는 문장으로 구성됩니다. 예측할 레이블은 0 또는 1입니다.\n",
        "\n",
        "텍스트를 표현하는 한 가지 방법은 문장을 임베딩(embedding) 벡터로 바꾸는 것입니다. 그러면 첫 번째 층으로 사전 훈련(pre-trained)된 텍스트 임베딩을 사용할 수 있습니다. 여기에는 두 가지 장점이 있습니다.\n",
        "\n",
        "* 텍스트 전처리에 대해 신경 쓸 필요가 없습니다.\n",
        "* 전이 학습의 장점을 이용합니다.\n",
        "* 임베딩은 고정 크기이기 때문에 처리 과정이 단순해집니다.\n",
        "\n",
        "이 예제는 [텐서플로 허브](https://www.tensorflow.org/hub)에 있는 **사전 훈련된 텍스트 임베딩 모델**인 [google/tf2-preview/gnews-swivel-20dim/1](https://tfhub.dev/google/tf2-preview/gnews-swivel-20dim/1)을 사용하겠습니다.\n",
        "\n",
        "테스트해 볼 수 있는 사전 훈련된 모델이 세 개 더 있습니다:\n",
        "\n",
        "* [google/tf2-preview/gnews-swivel-20dim-with-oov/1](https://tfhub.dev/google/tf2-preview/gnews-swivel-20dim-with-oov/1) - [google/tf2-preview/gnews-swivel-20dim/1](https://tfhub.dev/google/tf2-preview/gnews-swivel-20dim/1)와 동일하지만 어휘 사전(vocabulary)의 2.5%가 OOV 버킷(bucket)으로 변환되었습니다. 이는 해당 문제의 어휘 사전과 모델의 어휘 사전이 완전히 겹치지 않을 때 도움이 됩니다.\n",
        "* [google/tf2-preview/nnlm-en-dim50/1](https://tfhub.dev/google/tf2-preview/nnlm-en-dim50/1) - 더 큰 모델입니다. 차원 크기는 50이고 어휘 사전의 크기는 1백만 개 이하입니다.\n",
        "* [google/tf2-preview/nnlm-en-dim128/1](https://tfhub.dev/google/tf2-preview/nnlm-en-dim128/1) - 훨씬 더 큰 모델입니다. 차원 크기는 128이고 어휘 사전의 크기는 1백만 개 이하입니다."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "In2nDpTLkgKa"
      },
      "source": [
        "먼저 문장을 임베딩시키기 위해 텐서플로 허브 모델을 사용하는 케라스 층을 만들어 보죠. 그다음 몇 개의 샘플을 입력하여 테스트해 보겠습니다. 입력 텍스트의 길이에 상관없이 임베딩의 출력 크기는 `(num_examples, embedding_dimension)`가 됩니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "colab": {},
        "colab_type": "code",
        "id": "_NUbzVeYkgcO"
      },
      "outputs": [],
      "source": [
        "embedding = \"https://tfhub.dev/google/tf2-preview/gnews-swivel-20dim/1\"\n",
        "hub_layer = hub.KerasLayer(embedding, input_shape=[], \n",
        "                           dtype=tf.string, trainable=True)\n",
        "hub_layer(train_examples_batch[:3])"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "dfSbV6igl1EH"
      },
      "source": [
        "이제 전체 모델을 만들어 보겠습니다:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "colab": {},
        "colab_type": "code",
        "id": "xpKOoWgu-llD"
      },
      "outputs": [],
      "source": [
        "model = tf.keras.Sequential()\n",
        "model.add(hub_layer)\n",
        "model.add(tf.keras.layers.Dense(16, activation='relu'))\n",
        "model.add(tf.keras.layers.Dense(1, activation='sigmoid'))\n",
        "\n",
        "model.summary()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "6PbKQ6mucuKL"
      },
      "source": [
        "순서대로 층을 쌓아 분류기를 만듭니다:\n",
        "\n",
        "1. 첫 번째 층은 텐서플로 허브 층입니다. 이 층은 사전 훈련된 모델을 사용하여 하나의 문장을 임베딩 벡터로 매핑합니다. 여기서 사용하는 사전 훈련된 텍스트 임베딩 모델([google/tf2-preview/gnews-swivel-20dim/1](https://tfhub.dev/google/tf2-preview/gnews-swivel-20dim/1))은 하나의 문장을 토큰(token)으로 나누고 각 토큰의 임베딩을 연결하여 반환합니다. 최종 차원은 `(num_examples, embedding_dimension)`입니다.\n",
        "2. 이 고정 크기의 출력 벡터는 16개의 은닉 유닛(hidden unit)을 가진 완전 연결 층(`Dense`)으로 주입됩니다.\n",
        "3. 마지막 층은 하나의 출력 노드를 가진 완전 연결 층입니다. `sigmoid` 활성화 함수를 사용하므로 확률 또는 신뢰도 수준을 표현하는 0~1 사이의 실수가 출력됩니다.\n",
        "\n",
        "이제 모델을 컴파일합니다."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "L4EqVWg4-llM"
      },
      "source": [
        "### 손실 함수와 옵티마이저\n",
        "\n",
        "모델이 훈련하려면 손실 함수(loss function)과 옵티마이저(optimizer)가 필요합니다. 이 예제는 이진 분류 문제이고 모델이 확률을 출력하므로(출력층의 유닛이 하나이고 `sigmoid` 활성화 함수를 사용합니다), `binary_crossentropy` 손실 함수를 사용하겠습니다.\n",
        "\n",
        "다른 손실 함수를 선택할 수 없는 것은 아닙니다. 예를 들어 `mean_squared_error`를 선택할 수 있습니다. 하지만 일반적으로 `binary_crossentropy`가 확률을 다루는데 적합합니다. 이 함수는 확률 분포 간의 거리를 측정합니다. 여기에서는 정답인 타깃 분포와 예측 분포 사이의 거리입니다.\n",
        "\n",
        "나중에 회귀(regression) 문제(예를 들어 주택 가격을 예측하는 문제)에 대해 살펴 볼 때 평균 제곱 오차(mean squared error) 손실 함수를 어떻게 사용하는지 알아 보겠습니다.\n",
        "\n",
        "이제 모델이 사용할 옵티마이저와 손실 함수를 설정해 보죠:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "colab": {},
        "colab_type": "code",
        "id": "Mr0GP-cQ-llN"
      },
      "outputs": [],
      "source": [
        "model.compile(optimizer='adam',\n",
        "              loss='binary_crossentropy',\n",
        "              metrics=['accuracy'])"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "35jv_fzP-llU"
      },
      "source": [
        "## 모델 훈련\n",
        "\n",
        "이 모델을 512개의 샘플로 이루어진 미니배치(mini-batch)에서 20번의 에포크(epoch) 동안 훈련합니다. `x_train`과 `y_train` 텐서에 있는 모든 샘플에 대해 20번 반복한다는 뜻입니다. 훈련하는 동안 10,000개의 검증 세트에서 모델의 손실과 정확도를 모니터링합니다:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "colab": {},
        "colab_type": "code",
        "id": "tXSGrjWZ-llW"
      },
      "outputs": [],
      "source": [
        "history = model.fit(train_data.shuffle(10000).batch(512),\n",
        "                    epochs=20,\n",
        "                    validation_data=validation_data.batch(512),\n",
        "                    verbose=1)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "9EEGuDVuzb5r"
      },
      "source": [
        "## 모델 평가\n",
        "\n",
        "모델의 성능을 확인해 보죠. 두 개의 값이 반환됩니다. 손실(오차를 나타내는 숫자이므로 낮을수록 좋습니다)과 정확도입니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 0,
      "metadata": {
        "colab": {},
        "colab_type": "code",
        "id": "zOMKywn4zReN"
      },
      "outputs": [],
      "source": [
        "results = model.evaluate(test_data.batch(512), verbose=2)\n",
        "for name, value in zip(model.metrics_names, results):\n",
        "  print(\"%s: %.3f\" % (name, value))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "z1iEXVTR0Z2t"
      },
      "source": [
        "이 예제는 매우 단순한 방식을 사용하므로 87% 정도의 정확도를 달성했습니다. 고급 방법을 사용한 모델은 95%에 가까운 정확도를 얻습니다."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "5KggXVeL-llZ"
      },
      "source": [
        "## 더 읽을거리\n",
        "\n",
        "문자열 입력을 다루는 조금 더 일반적인 방법과 훈련 과정에서 정확도나 손실 변화에 대한 자세한 분석은 [여기](https://www.tensorflow.org/tutorials/keras/basic_text_classification)를 참고하세요."
      ]
    }
  ],
  "metadata": {
    "colab": {
      "collapsed_sections": [],
      "name": "text_classification_with_hub.ipynb",
      "private_outputs": true,
      "provenance": [],
      "toc_visible": true
    },
    "kernelspec": {
      "display_name": "Python 3",
      "language": "python",
      "name": "python3"
    },
    "language_info": {
      "codemirror_mode": {
        "name": "ipython",
        "version": 3
      },
      "file_extension": ".py",
      "mimetype": "text/x-python",
      "name": "python",
      "nbconvert_exporter": "python",
      "pygments_lexer": "ipython3",
      "version": "3.6.7"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 0
}
